/*
* Copyright (c) Microsoft Corporation.  All Rights Reserved.
*
* Public Mock10 API
*/
#pragma once
#include "Mock10.h"
#include "MockAssert.h"
#include "MockStatus.h"
#include "Detour.h"

#if defined(NO_MOCK_VALIDATION) && defined(VALIDATE_TARGET_FUNCTION_POINTERS)
    COMPILE_TIME_CHECK_V2(false, 
            "VALIDATE_TARGET_FUNCTION_POINTERS is not valid when NO_MOCK_VALIDATION is also defined.",
            VALIDATE_TARGET_FUNCTION_POINTERS_is_not_valid_when_NO_MOCK_VALIDATION_is_also_defined);
#endif

#if defined(NO_MOCK_VALIDATION) && defined(VALIDATE_DETOUR_FUNCTIONS)
    COMPILE_TIME_CHECK_V2(false, 
            "VALIDATE_DETOUR_FUNCTIONS is not valid when NO_MOCK_VALIDATION is also defined.",
            VALIDATE_DETOUR_FUNCTIONS_is_not_valid_when_NO_MOCK_VALIDATION_is_also_defined);
#endif

#pragma push_macro("Assert")
#undef Assert

#pragma push_macro("SEALED")
#undef SEALED

#if _MSC_VER >= 1400
# define SEALED sealed
#else
# define SEALED
#endif

// Disable mock exception usage if exceptions are disabled for this project
#if !defined(_CPPUNWIND)
#if !defined(NO_MOCK_THROW)
    #define NO_MOCK_THROW
#endif
#endif

#if defined(_CPPUNWIND)
#if !defined(NO_MOCK_THROW)
    #include "MockException.h"
#endif // NO_MOCK_THROW is not defined
#endif //_CPPUNWIND is defined

#if !defined(MOCK10_STATIC)
    #pragma comment(lib, "Mock10.lib")
#endif

namespace Mock10
{
    template <typename TPrototype, typename TDetourPrototype, typename TDetourFunctionImpl>
    class MockFunctionImpl;

    template <typename TPrototype, typename TDetourPrototype, typename TDetourFunctionImpl>
    class MockFunction;

    template <typename TPrototype, typename TDetourPrototype, typename TDetourFunctionImpl>
    class MockMethod;

    namespace Private
    {
        class DetourFunctionBase;

        template <typename TPrototype, typename TDetourPrototype>
        class ExternalMockFunctionInstance;

        template <typename TMockImpl>
        class ExternalMockFunctionInstanceImpl;

        class ExternalMockFunctionInstanceBase;

        class MOCK10_API DetourInfo SEALED
        {
        template <typename TPrototype, typename TDetourPrototype, typename TDetourFunctionImpl>
        friend class MockFunctionImpl;
        template <typename TPrototype, typename TDetourPrototype, typename TDetourFunctionImpl>
        friend class MockFunction;
        template <typename TPrototype, typename TDetourPrototype, typename TDetourFunctionImpl>
        friend class MockMethod;
        template <typename T>
        friend class WEX::Common::PtrRefCount;
        friend class DetourInfoArray;

        private:
            DetourInfo(DetourFunctionBase* pDetourFunction);
            ~DetourInfo();
            DetourFunctionBase& GetDetour();
            unsigned long AddRef();
            unsigned long Release();

            DetourFunctionBase* m_pDetourFunction;
            volatile long m_refCount;
        };

        class MOCK10_API DetourInfoArray SEALED
        {
        template <typename TPrototype, typename TDetourPrototype, typename TDetourFunctionImpl>
        friend class Mock10::MockFunctionImpl;

        template <typename TPrototype, typename TDetourPrototype, typename TDetourFunctionImpl>
        friend class Mock10::MockFunction;

        template <typename TPrototype, typename TDetourPrototype, typename TDetourFunctionImpl>
        friend class Mock10::MockMethod;

        private:
            DetourInfoArray();
            ~DetourInfoArray();
            DetourInfoArray(const DetourInfoArray&);

            DetourInfoArray& operator=(const DetourInfoArray&); // Not implemented

            void Clear();
            MockStatus Add(DetourFunctionBase* pDetour);

            size_t GetCount() const;
            DetourInfo* operator[](size_t index) const;

            struct Impl;
            Impl* m_pImpl;
            MockStatus m_status;
        };

        MOCK10_API MockStatus __stdcall ValidateMatchCount(size_t expected, size_t actual);
        MOCK10_API HRESULT __stdcall TargetFromProcAddress(const wchar_t* pszTargetBinary, const wchar_t* pszTargetQuery, void*& pTargetFunction, HMODULE& moduleHandle);
        MOCK10_API void* __stdcall ResolveFunctionPointer(void* pFunction); // Ensures that we have a pointer to the true function, not a stub/forwarding function compiled into the binary

        // wchar_t native type exports
        #if defined(MOCK10_FULL_BUILD)
        MOCK10_API HRESULT __stdcall TargetFromProcAddress(const __wchar_t* pszTargetBinary, const __wchar_t* pszTargetQuery, void*& pTargetFunction, HMODULE& moduleHandle);
        #endif
    } /* namespace Private */

    class MOCK10_API TargetFunction abstract
    {
    public:
        TargetFunction(const TargetFunction&); // Copy constructor necessary to return from GetTarget()
        virtual ~TargetFunction();
        HRESULT GetStatus() const;

    protected:
        explicit TargetFunction(HRESULT status);
        explicit TargetFunction(void* pFunction);

    private:
        TargetFunction& operator=(const TargetFunction&); // Not implemented

        struct Impl;
        Impl* m_pImpl;
        HRESULT m_status;
    };

    class __declspec(novtable) MOCK10_API MockFunctionBase
    {
    UNIT_TEST_CLASS(WEX::UnitTests::MockFunctionTests)

    public:
        virtual ~MockFunctionBase();
        MockFunctionBase(const MockFunctionBase&);

        HRESULT GetStatus() const
        {
            return m_status.GetErrorCode();
        }

        const unsigned short* GetStatusMessage() const // Return unsigned short* here; otherwise we'd need to create both wchar_t* and __wchar_t* versions that return the value as an out parameter
        {
            return m_status.GetMessage();
        }

        virtual void Clear() = 0;
        virtual void SetFilter(const wchar_t* pszFilter) = 0;

    protected:
        MockFunctionBase();
        
        void ValidateSignature(void* pFunction1, void* pFunction2, bool strictComparison = true);
        WEX::Common::DebugHelp& GetDebugHelp();

        bool m_shouldValidate;
        Private::MockStatus m_status;

    private:
        MockFunctionBase& operator=(const MockFunctionBase&); // Not implemented

        struct Impl;
        Impl* m_pImpl;
    };

    template <typename TPrototype, typename TDetourPrototype = TPrototype, typename TDetourFunctionImpl = Private::DetourFunction<TPrototype, TDetourPrototype>>
    class MockFunctionImpl: public MockFunctionBase
    {
    public:
        typedef TPrototype Prototype; // Ease-of-use typedef of the function prototype for clients

        typedef TDetourFunctionImpl TDetourFunctionImpl; // typedefs used for external mocking purposes
        typedef TDetourPrototype TDetourPrototype;

        virtual ~MockFunctionImpl(){}

        template <typename TDetourFunction>
        void Set(TDetourFunction* pDetourFunction)
        {
            Private::AutoDisableDetours disable;
            Replace(PrivateUtilities::ResolveFunctionPointer(pDetourFunction));
        }

        template <typename TFunctor>
        void Set(const TFunctor& detourFunctor)
        {
            Private::AutoDisableDetours disable;
            Replace(WEX::Common::Functor<TDetourPrototype>(detourFunctor));
        }

        void Clear()
        {
            Private::AutoDisableDetours disable;
            // Explicitly specify TDetourPrototype* so Replace treats null like a function pointer
            Replace<TDetourPrototype*>(NULL);
        }

        void SetFilter(const wchar_t* pszFilter)
        {
            Private::AutoDisableDetours disable;
            if (SUCCEEDED(m_status.GetErrorCode()))
            {
                // Iterate over all detours and set their filters
                size_t count = m_detours.GetCount();
                for (size_t i = 0; i < count && SUCCEEDED(m_status.GetErrorCode()); ++i)
                {
                    Private::DetourInfo* pInfo = m_detours[i];
                    WEX_ASSERT(pInfo, L"");
                    
                    if (!pInfo)
                    {
                        m_status.Set(E_UNEXPECTED);
                        break;
                    }

                    TDetourFunctionImpl& detour = static_cast<TDetourFunctionImpl&>(pInfo->GetDetour());
                    detour.SetFilter(pszFilter);
                    m_status = detour.GetStatus();
                }

                if (FAILED(m_status.GetErrorCode()))
                {
                    ClearDetours();
                }
            }

            #if !defined(NO_MOCK_THROW)
            if (FAILED(m_status.GetErrorCode()))
            {
                Private::Throw::MockException(m_status);
            }
            #endif
        }

    protected:
        MockFunctionImpl(TPrototype* pTargetFunction)
        {
            Private::AutoDisableDetours disable;
            Initialize<TDetourPrototype*>(PrivateUtilities::ResolveFunctionPointer(pTargetFunction), NULL); // Explicitly specify TDetourPrototype* so Initialize treats null like a function pointer
        }

        template <typename TDetourFunction>
        MockFunctionImpl(TPrototype* pTargetFunction, TDetourFunction* pDetourFunction)
        {
            Private::AutoDisableDetours disable;
            Initialize(PrivateUtilities::ResolveFunctionPointer(pTargetFunction), PrivateUtilities::ResolveFunctionPointer(pDetourFunction));
        }

        template <typename TFunctor>
        MockFunctionImpl(TPrototype* pTargetFunction, const TFunctor& detourFunctor)
        {
            Private::AutoDisableDetours disable;
            Initialize(PrivateUtilities::ResolveFunctionPointer(pTargetFunction), WEX::Common::Functor<TDetourPrototype>(detourFunctor));
        }

        MockFunctionImpl(const wchar_t* pszTargetBinary, const wchar_t* pszTargetQuery, unsigned long expectedMatchCount = 1)
        {
            Private::AutoDisableDetours disable;
            InitializeFromQuery<TDetourPrototype*>(pszTargetBinary, pszTargetQuery, NULL, expectedMatchCount); // Explicitly specify TDetourPrototype* so InitializeFromQuery treats null like a function pointer
        }

        template <typename TDetourFunction>
        MockFunctionImpl(const wchar_t* pszTargetBinary, const wchar_t* pszTargetQuery, TDetourFunction* pDetourFunction, unsigned long expectedMatchCount = 1)
        {
            Private::AutoDisableDetours disable;
            InitializeFromQuery(pszTargetBinary, pszTargetQuery, PrivateUtilities::ResolveFunctionPointer(pDetourFunction), expectedMatchCount);
        }

        template <typename TFunctor>
        MockFunctionImpl(const wchar_t* pszTargetBinary, const wchar_t* pszTargetQuery, const TFunctor& detourFunctor, unsigned long expectedMatchCount = 1)
        {
            Private::AutoDisableDetours disable;
            InitializeFromQuery(pszTargetBinary, pszTargetQuery, WEX::Common::Functor<TDetourPrototype>(detourFunctor), expectedMatchCount);
        }

        MockFunctionImpl(const Private::TargetFunctionInfoArray& targetFunctions, TDetourPrototype* pDetourFunction)
        {
            Private::AutoDisableDetours disable;
            InitializeFromTargetFunctionInfo(targetFunctions, pDetourFunction);
        }

        // Only called/used by external mock instances
        void SetOwner(Private::ExternalMockFunctionInstanceBase& owner)
        {
            if (SUCCEEDED(m_status.GetErrorCode()))
            {
                // Iterate over all detours and set their owner
                size_t count = m_detours.GetCount();
                for (size_t i = 0; i < count; ++i)
                {
                    Private::DetourInfo* pInfo = m_detours[i];
                    WEX_ASSERT(pInfo, L"NULL DetourInfo was returned from m_detours[i].");
                    
                    if (!pInfo)
                    {
                        m_status.Set(E_UNEXPECTED);
                        break;
                    }

                    TDetourFunctionImpl& detour = static_cast<TDetourFunctionImpl&>(pInfo->GetDetour());
                    detour.SetOwner(owner);
                }
            }
        }

        Private::DetourInfoArray m_detours;

    private:
        MockFunctionImpl& operator=(const MockFunctionImpl&); // Not implemented

        void ValidateTargetFunction(TPrototype* pTargetFunction)
        {
            UNREFERENCED_PARAMETER(pTargetFunction);

        #if !defined(NO_MOCK_VALIDATION)
            if (SUCCEEDED(m_status.GetErrorCode()))
            {
                ValidateSignature(pTargetFunction, Private::ResolveFunctionPointer(Private::DetourShim<TPrototype, TDetourFunctionImpl>::ShimPointer()));
            }
        #endif
        }

        template <typename TDetourFunction>
        void ValidateDetourFunction(TDetourFunction* pDetourFunction)
        {
            // We need to strip off the calling convention for the detour, because it will just be called by the functor anyway; no need to validate it -- we just want to validate parameters
            COMPILE_TIME_CHECK_V2((WEX::Common::AreSameType<WEX::Common::ParameterTypes<TDetourPrototype>::RawPrototype, WEX::Common::ParameterTypes<TDetourFunction>::RawPrototype>::value), 
                    "The detour function specified does not have the expected function signature. Please recheck its signature",
                    The_detour_function_specified_does_not_have_the_expected_function_signature_Please_recheck_its_signature);

            UNREFERENCED_PARAMETER(pDetourFunction);

            #if !defined(NO_MOCK_VALIDATION)
            // It is valid to create a detour with a null pDetourFunction
            if (pDetourFunction)
            {
                #if defined(VALIDATE_DETOUR_FUNCTIONS)
                // It is valid to have a detour whose calling convention is different than the target, because it will be invoked
                // via a function pointer from within the Functor class -- it's the DetourShim which must have an identical calling convention to the target.
                // So the check below simply validates that the detour function matches the signature that it claims to be.
                ValidateSignature(pDetourFunction, Private::ResolveFunctionPointer(Private::DetourShim<TDetourFunction, Private::DetourFunction<TDetourFunction>>::ShimPointer()), false);
                #endif
            }
            #endif
        }

        void ValidateDetourFunction(const WEX::Common::Functor<TDetourPrototype>& detourFunctor)
        {
            // If we've ended up with an invalid detour Functor, we must have run out of memory
            if (!detourFunctor.IsValid())
            {
                m_status.Set(E_OUTOFMEMORY);
            }
        }

        template <typename TDetourFunction>
        void Initialize(TPrototype* pTargetFunction, const TDetourFunction& detourFunction)
        {
            if (SUCCEEDED(m_status.GetErrorCode()))
            {
                // Don't bother doing anything with a NULL pTargetFunction
                if (!pTargetFunction)
                {
                    m_status.Set(E_POINTER);
                }
                else
                {
                    // Only perform target function validation in this path if explicitly requested
                    #if defined(VALIDATE_TARGET_FUNCTION_POINTERS)
                    ValidateTargetFunction(pTargetFunction);
                    #endif

                    if (SUCCEEDED(m_status.GetErrorCode()))
                    {
                        ValidateDetourFunction(detourFunction);
                        if (SUCCEEDED(m_status.GetErrorCode()))
                        {
                            AddDetour(pTargetFunction, detourFunction);
                        }
                    }
                }
            }

            #if !defined(NO_MOCK_THROW)
            if (FAILED(m_status.GetErrorCode()))
            {
                Private::Throw::MockException(m_status);
            }
            #endif
        }

        template <typename TDetourFunction>
        void InitializeFromQuery(const wchar_t* pszTargetBinary, const wchar_t* pszTargetQuery, const TDetourFunction& detourFunction, unsigned long expectedMatchCount)
        {
            // Don't bother doing any work unless the detour is valid
            ValidateDetourFunction(detourFunction);
            if (SUCCEEDED(m_status.GetErrorCode()))
            {
                HMODULE moduleHandle = NULL;
                void* pTargetFunction = NULL;
                if ((expectedMatchCount == 1) && SUCCEEDED(Private::TargetFromProcAddress(pszTargetBinary, pszTargetQuery, pTargetFunction, moduleHandle)))
                {
                    // Make sure we have the actual function pointer, and not a pointer to a stub function, then add the detour
                    pTargetFunction = PrivateUtilities::ResolveFunctionPointer(static_cast<TPrototype*>(pTargetFunction));
                    WEX_ASSERT(pTargetFunction, L"Resolved pTargetFunction should not be NULL.");

                    ValidateTargetFunction(static_cast<TPrototype*>(pTargetFunction));
                    if (SUCCEEDED(m_status.GetErrorCode()))
                    {
                        AddDetour(static_cast<TPrototype*>(pTargetFunction), detourFunction);
                    }

                    // TargetFromProcAddress transferred ownership of the moduleHandle to us, so we need to free it here
                    ::FreeLibrary(moduleHandle);
                }
                else
                {
                    // Resolve the query with DebugHelp
                    Private::TargetFunctionInfoArray targetFunctions;
                    m_status = Private::ResolveQuery(pszTargetBinary, pszTargetQuery, targetFunctions, Private::DetourShim<TPrototype, TDetourFunctionImpl>::ClassPointer(), GetDebugHelp());
                    if (SUCCEEDED(m_status.GetErrorCode()))
                    {
                        // We don't need to validate any target signatures, because we already trim invalid target functions from the list
                        m_status = Private::TrimUnneeded(targetFunctions, PrivateUtilities::GetDetourPointer(detourFunction), 
                                                         PrivateUtilities::ResolveFunctionPointer(Private::DetourShim<TPrototype, TDetourFunctionImpl>::ShimPointer()), GetDebugHelp());
                        if (SUCCEEDED(m_status.GetErrorCode()))
                        {
                            size_t count = targetFunctions.GetCount();
                            m_status = Private::ValidateMatchCount(expectedMatchCount, count);
                            if (SUCCEEDED(m_status.GetErrorCode()))
                            {
                                InitializeFromTargetFunctionInfo(targetFunctions, detourFunction);
                            }
                        }
                    }
                }
            }

            #if !defined(NO_MOCK_THROW)
            if (FAILED(m_status.GetErrorCode()))
            {
                Private::Throw::MockException(m_status);
            }
            #endif
        }

        template <typename TDetourFunction>
        void InitializeFromTargetFunctionInfo(const Private::TargetFunctionInfoArray& targetFunctions, const TDetourFunction& detourFunction)
        {
            size_t count = targetFunctions.GetCount();
            for (size_t i = 0; i < count; ++i)
            {
                Private::TargetFunctionInfo* pTargetFunctionInfo = targetFunctions.GetAt(i);
                WEX_ASSERT(pTargetFunctionInfo, L"NULL DetourInfo was returned from m_detours[i].");

                if (!pTargetFunctionInfo)
                {
                    m_status.Set(E_UNEXPECTED);
                }
                else
                {
                    AddDetour(*pTargetFunctionInfo, detourFunction);
                }

                if (FAILED(m_status.GetErrorCode()))
                {
                    ClearDetours();
                    break;
                }
            }

            #if !defined(NO_MOCK_THROW)
            if (FAILED(m_status.GetErrorCode()))
            {
                Private::Throw::MockException(m_status);
            }
            #endif
        }

        template <typename TDetourFunction>
        void Replace(const TDetourFunction& detourFunction)
        {
            // We only need to validate the detour function, since the targets were validated during construction
            ValidateDetourFunction(detourFunction);
            if (SUCCEEDED(m_status.GetErrorCode()))
            {
                // Iterate over all detours and replace their detour functions
                size_t count = m_detours.GetCount();
                for (size_t i = 0; i < count && SUCCEEDED(m_status.GetErrorCode()); ++i)
                {
                    Private::DetourInfo* pInfo = m_detours[i];
                    WEX_ASSERT(pInfo, L"");
                    
                    if (!pInfo)
                    {
                        m_status.Set(E_UNEXPECTED);
                        break;
                    }

                    TDetourFunctionImpl& detour = static_cast<TDetourFunctionImpl&>(pInfo->GetDetour());
                    detour.Replace(detourFunction);
                    m_status = detour.GetStatus();
                }
            }

            #if !defined(NO_MOCK_THROW)
            if (FAILED(m_status.GetErrorCode()))
            {
                Private::Throw::MockException(m_status);
            }
            #endif
        }

        template <typename TDetourFunction>
        void AddDetour(TPrototype* pTargetFunction, const TDetourFunction& detourFunction)
        {
            Private::TargetFunctionInfo targetFunctionInfo(pTargetFunction);
            AddDetour(targetFunctionInfo, detourFunction);
        }

        template <typename TDetourFunction>
        void AddDetour(Private::TargetFunctionInfo& targetFunctionInfo, const TDetourFunction& detourFunction)
        {
            TDetourFunctionImpl* pDetour = new TDetourFunctionImpl(targetFunctionInfo, detourFunction);
            if (pDetour)
            {
                m_status = pDetour->GetStatus();
                if (SUCCEEDED(m_status.GetErrorCode()))
                {
                    m_status = m_detours.Add(pDetour);
                }
                else
                {
                    delete pDetour;
                }
            }
            else
            {
                m_status.Set(E_OUTOFMEMORY);
            }
        }

        void ClearDetours()
        {
            m_detours.Clear();
        }

        struct PrivateUtilities
        {
            static TDetourPrototype* GetDetourPointer(TDetourPrototype* pDetourFunction)
            {
                return pDetourFunction;
            }

            // We can't really convert the Functor to a function pointer, so we can just return null for these
            static void* GetDetourPointer(const WEX::Common::Functor<TPrototype>&)
            {
                return NULL;
            }

            template <typename TPrototype>
            static TPrototype* ResolveFunctionPointer(TPrototype* pFunction)
            {
                return static_cast<TPrototype*>(Private::ResolveFunctionPointer(pFunction));
            }
        };
    };

    template <typename TPrototype, typename TDetourPrototype = TPrototype, typename TDetourFunctionImpl = Private::DetourFunction<TPrototype, TDetourPrototype>>
    class MockFunction: public MockFunctionImpl<TPrototype, TDetourPrototype, TDetourFunctionImpl>
    {
    template <typename TPrototype, typename TDetourPrototype>
    friend class Private::ExternalMockFunctionInstance;

    template <typename TMockImpl>
    friend class Private::ExternalMockFunctionInstanceImpl;

    public:
        MockFunction(TPrototype* pTargetFunction)
        : MockFunctionImpl(pTargetFunction)
        {
        }

        template <typename TDetourFunction>
        MockFunction(TPrototype* pTargetFunction, TDetourFunction* pDetourFunction)
        : MockFunctionImpl(pTargetFunction, pDetourFunction)
        {
        }

        template <typename TFunctor>
        MockFunction(TPrototype* pTargetFunction, const TFunctor& detourFunctor)
        : MockFunctionImpl(pTargetFunction, detourFunctor)
        {
        }

        MockFunction(const wchar_t* pszTargetBinary, const wchar_t* pszTargetQuery, unsigned long expectedMatchCount = 1)
        : MockFunctionImpl(pszTargetBinary, pszTargetQuery, expectedMatchCount)
        {
        }

        template <typename TDetourFunction>
        MockFunction(const wchar_t* pszTargetBinary, const wchar_t* pszTargetQuery, TDetourFunction* pDetourFunction, unsigned long expectedMatchCount = 1)
        : MockFunctionImpl(pszTargetBinary, pszTargetQuery, pDetourFunction, expectedMatchCount)
        {
        }

        template <typename TFunctor>
        MockFunction(const wchar_t* pszTargetBinary, const wchar_t* pszTargetQuery, const TFunctor& detourFunctor, unsigned long expectedMatchCount = 1)
        : MockFunctionImpl(pszTargetBinary, pszTargetQuery, TFunctor& detourFunctor, expectedMatchCount)
        {
        }

        class Target: public TargetFunction
        {
        template <typename TPrototype, typename TDetourPrototype, typename TDetourFunctionImpl>
        friend class MockFunction;

        template <typename TPrototype>
        friend class ExternalMockFunction;

        public:
            Target(const Target& other)
            : TargetFunction(other)
            , m_pFunction(other.m_pFunction)
            {}

            #define MOCK_FUNCTION_TARGET_IMPL(PARAMCOUNT)                                                                                         \
            typename WEX::Common::ParameterTypes<TDetourPrototype>::ReturnType operator()(BUILD_VARIABLE_NAME(PARAMETER_NAMED_ARGS_, PARAMCOUNT)) \
            {                                                                                                                                     \
                WEX_ASSERT(m_pFunction, L"");                                                                                                     \
                return (*m_pFunction)(BUILD_VARIABLE_NAME(PARAMETER_ARGS_, PARAMCOUNT));                                                          \
            }

            MOCK_FUNCTION_TARGET_IMPL(0)
            MOCK_FUNCTION_TARGET_IMPL(1)
            MOCK_FUNCTION_TARGET_IMPL(2)
            MOCK_FUNCTION_TARGET_IMPL(3)
            MOCK_FUNCTION_TARGET_IMPL(4)
            MOCK_FUNCTION_TARGET_IMPL(5)
            MOCK_FUNCTION_TARGET_IMPL(6)
            MOCK_FUNCTION_TARGET_IMPL(7)
            MOCK_FUNCTION_TARGET_IMPL(8)
            MOCK_FUNCTION_TARGET_IMPL(9)
            MOCK_FUNCTION_TARGET_IMPL(10)
            MOCK_FUNCTION_TARGET_IMPL(11)
            MOCK_FUNCTION_TARGET_IMPL(12)
            MOCK_FUNCTION_TARGET_IMPL(13)
            MOCK_FUNCTION_TARGET_IMPL(14)
            MOCK_FUNCTION_TARGET_IMPL(15)
            MOCK_FUNCTION_TARGET_IMPL(16)
            MOCK_FUNCTION_TARGET_IMPL(17)
            MOCK_FUNCTION_TARGET_IMPL(18)
            MOCK_FUNCTION_TARGET_IMPL(19)
            MOCK_FUNCTION_TARGET_IMPL(20)

        private:
            Target& operator=(const Target&); // Not implemented

            Target(HRESULT status)
            : TargetFunction(status)
            , m_pFunction(NULL)
            {
            #if !defined(NO_MOCK_THROW)
                HRESULT hr = GetStatus();
                if (FAILED(hr))
                {
                    Private::Throw::MockException(hr);
                }
            #endif
            }

            Target(void* pFunction)
            : TargetFunction(pFunction)
            , m_pFunction(static_cast<TPrototype*>(pFunction))
            {
            #if !defined(NO_MOCK_THROW)
                HRESULT hr = GetStatus();
                if (FAILED(hr))
                {
                    Private::Throw::MockException(hr);
                }
            #endif
            }

            TPrototype* m_pFunction;
        };

        Target GetTarget(unsigned long targetIndex = 0) const
        {
            if (FAILED(m_status.GetErrorCode()))
            {
                return Target(m_status.GetErrorCode());
            }

            if (m_detours.GetCount() <= targetIndex)
            {
                return Target(HRESULT_FROM_WIN32(ERROR_INVALID_INDEX));
            }

            return Target(m_detours[targetIndex]->GetDetour().GetTarget());
        }

    private:
        MockFunction(const Private::TargetFunctionInfoArray& targetFunctions, TDetourPrototype* pDetourFunction)
        : MockFunctionImpl(targetFunctions, pDetourFunction)
        {
        }
    };
} /* namespace Mock10 */

#pragma pop_macro("SEALED")
#pragma pop_macro("Assert")
